﻿using Newtonsoft.Json;
using System.ComponentModel;
using System.Dynamic;

namespace Koala.Pro.Common
{
    public static class ObjectExtensions
    {
        public static object CastTo(this object value, Type conversionType)
        {
            if (value == null)
            {
                return null;
            }
            if (conversionType.IsNullableType())
            {
                conversionType = conversionType.GetUnNullableType();
            }
            if (conversionType.IsEnum)
            {
                return Enum.Parse(conversionType, value.ToString());
            }
            if (conversionType == typeof(Guid))
            {
                return Guid.Parse(value.ToString());
            }
            return Convert.ChangeType(value, conversionType);
        }

        public static T CastTo<T>(this object value)
        {
            if (value == null && default(T) == null)
            {
                return default(T);
            }
            if (value.GetType() == typeof(T))
            {
                return (T)value;
            }
            return (T)value.CastTo(typeof(T));
        }

        public static T CastTo<T>(this object value, T defaultValue)
        {
            try
            {
                return value.CastTo<T>();
            }
            catch (Exception)
            {
                return defaultValue;
            }
        }

        public static bool IsBetween<T>(this IComparable<T> value, T start, T end, bool leftEqual = true, bool rightEqual = true) where T : IComparable
        {
            if (leftEqual ? (value.CompareTo(start) >= 0) : (value.CompareTo(start) > 0))
            {
                if (!rightEqual)
                {
                    return value.CompareTo(end) < 0;
                }
                return value.CompareTo(end) <= 0;
            }
            return false;
        }

        public static bool IsInRange<T>(this IComparable<T> value, T min, T max, bool minEqual = true, bool maxEqual = true) where T : IComparable
        {
            if (minEqual ? (value.CompareTo(min) >= 0) : (value.CompareTo(min) > 0))
            {
                if (!maxEqual)
                {
                    return value.CompareTo(max) < 0;
                }
                return value.CompareTo(max) <= 0;
            }
            return false;
        }

        public static bool IsIn<T>(this T value, params T[] source)
        {
            return source.Contains(value);
        }

        public static dynamic ToDynamic(this object value)
        {
            IDictionary<string, object> dictionary = new ExpandoObject();
            foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(value.GetType()))
            {
                object value2 = property.GetValue(value);
                if (property.PropertyType.FullName != null && property.PropertyType.FullName.StartsWith("<>f__AnonymousType"))
                {
                    dynamic val = value2.ToDynamic();
                    dictionary.Add(property.Name, val);
                }
                else
                {
                    dictionary.Add(property.Name, value2);
                }
            }
            return (ExpandoObject)dictionary;
        }

        public static long ToLong(this object o, long t = 0L)
        {
            if (!long.TryParse(o.ToString(string.Empty), out var result))
            {
                return t;
            }
            return result;
        }

        public static string ToString(this object o, string t)
        {
            string empty = string.Empty;
            if (o == null)
            {
                return t;
            }
            return o.ToString();
        }

        public static void SetProperty(this object obj, string name, object value)
        {
            foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(obj))
            {
                if (property.Name == name)
                {
                    Type propertyType = property.PropertyType;
                    if (!propertyType.IsGenericType)
                    {
                        value = Convert.ChangeType(value, propertyType);
                    }
                    else if (propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                    {
                        value = Convert.ChangeType(value, Nullable.GetUnderlyingType(propertyType));
                    }
                    property.SetValue(obj, value);
                }
            }
        }

        public static object GetProperty(this object obj, string name)
        {
            object result = null;
            foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(obj))
            {
                if (property.Name == name)
                {
                    result = property.GetValue(obj);
                }
            }
            return result;
        }

        public static List<string> GetPropertyNames(this object obj)
        {
            List<string> list = new List<string>();
            foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(obj))
            {
                list.Add(property.Name);
            }
            return list;
        }

        public static Dictionary<string, object> GetPropertyDict(this object obj)
        {
            Dictionary<string, object> dictionary = new Dictionary<string, object>();
            foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(obj))
            {
                object value = property.GetValue(obj);
                if (!dictionary.ContainsKey(property.Name))
                {
                    dictionary.Add(property.Name, value);
                }
            }
            return dictionary;
        }

        public class Variance
        {
            public string Property { get; set; }

            public string ValueA { get; set; }

            public string ValueB { get; set; }
        }

        public static List<Variance> DetailedCompare<T>(this T val1, T val2)
        {
            return (from f in val1.GetType().GetProperties()
                    select new Variance
                    {
                        Property = f.Name,
                        ValueA = (f.GetValue(val1, null)?.ToString() ?? ""),
                        ValueB = (f.GetValue(val2, null)?.ToString() ?? "")
                    } into v
                    where !v.ValueA.Equals(v.ValueB)
                    select v).ToList();
        }

        public static string GetChangedNote<T>(this T oldVal, T newVal)
        {
            List<string> specialList = new List<string> { "edittime", "createtime", "lastupdated" };
            var list = (from s in oldVal.DetailedCompare(newVal)
                        select new
                        {
                            Property = s.Property,
                            OldValue = s.ValueA,
                            NewValue = s.ValueB
                        } into s
                        where !specialList.Contains(s.Property.ToLower())
                        select s).ToList();
            string result = null;
            if (list != null && list.Count > 0)
            {
                string text = EntityHelper.GetEntityId(oldVal)?.ToString();
                list.Add(new
                {
                    Property = "Id",
                    OldValue = text,
                    NewValue = text
                });
                result = JsonConvert.SerializeObject(list, Formatting.Indented);
            }
            return result;
        }
    }

}
